/*
 * Copyright:  2018 smarabbit studio.
 *
 * Licensed under the Confluent Community License; you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 *
 * http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under
 *  the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 *  either express or implied.  See the License for the specific language governing
 *  permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:    2020/8/15 下午4:01
 *
 */
package com.massyframework.beanskin.graphql.fetching;

import graphql.execution.MergedField;
import graphql.language.*;
import graphql.schema.*;

import java.util.*;

/**
 * {@link AbstactMergedFieldFactory}, 抽象的MergedFieldFactory
 * @param <T>
 */
public abstract class AbstactMergedFieldFactory<T> implements MergedFieldFactory<T> {

    public AbstactMergedFieldFactory() {
    }

    /**
     * 创建新的组合字段
     * @param currentField {@link MergedField}, 当前的组合字段
     * @param fieldMap {@link Map}
     * @return {@link MergedField}
     */
    protected MergedField doCreate(MergedField currentField, Map<String, CustomizeField> fieldMap){
        Field singleField = currentField.getSingleField();
        SelectionSet ss = singleField.getSelectionSet();
        SelectionSet selectionSet =
                SelectionSet.newSelectionSet()
                        .sourceLocation(ss.getSourceLocation())
                        .additionalData(ss.getAdditionalData())
                        .comments(ss.getComments())
                        .selections(fieldMap.values())
                        .build();
        Field newField = Field.newField(singleField.getName())
                .arguments(singleField.getArguments())
                .alias(singleField.getAlias())
                .directives(singleField.getDirectives())
                .selectionSet(selectionSet)
                .build();
        return MergedField.newMergedField(newField).build();
    }

    /**
     * 解析{@code currentField}已经存有的字段
     * @param currentField {@link MergedField}
     * @return {@link Map}
     */
    protected Map<String, CustomizeField> parseExistFields(MergedField currentField){
        Map<String, CustomizeField> result = new LinkedHashMap<>();

        SelectionSet selectionSet = currentField.getSingleField().getSelectionSet();
        for (Selection<?> selection: selectionSet.getSelections()){
            if (selection instanceof Field){

                Field field = (Field)selection;
                CustomizeField customizeField = CustomizeField.newBuilder(field).build();
                result.put(customizeField.getName(), customizeField);
            }
        }

        return result;
    }

    /**
     * 对{@code objectType}的字段进行遍历，向{@code fieldMap}填充标量和枚举字段
     * @param objectType
     * @param fieldMap
     */
    protected void fullEnumAndScalarField(GraphQLObjectType objectType, Map<String, CustomizeField> fieldMap){
        List<GraphQLFieldDefinition> definitions = objectType.getFieldDefinitions();
        for (GraphQLFieldDefinition definition: definitions){
            String name = definition.getName();
            if (fieldMap.containsKey(name)){
                continue;
            }

            if (definition.getArguments().size() > 0){
                //有参数，不能自动扩展
                continue;
            }

            GraphQLType type = definition.getType();
            if (type instanceof GraphQLNonNull){
                type = ((GraphQLNonNull)type).getWrappedType();
            }

            if (type instanceof GraphQLScalarType){
                CustomizeField field =
                        this.createCustomizeField(name, (GraphQLScalarType)type);
                if (field != null) {
                    fieldMap.put(field.getName(), field);
                }
                continue;
            }

            if (type instanceof GraphQLEnumType){
                CustomizeField field =
                        this.createCustomizeField(name, (GraphQLEnumType)type);
                if (field != null) {
                    fieldMap.put(field.getName(), field);
                }
                continue;
            }
        }
    }

    /**
     * 创建标量自定义字段
     * @param name {@link String}, 字段名称
     * @param scalarType {@link GraphQLScalarType}, 标量类型
     * @return {@link CustomizeField}
     */
    protected CustomizeField createCustomizeField(String name, GraphQLScalarType scalarType){
        ScalarTypeDefinition definition = scalarType.getDefinition();
        if (definition != null) {
            return CustomizeField.newBuilder(name)
                    .ignoredChars(definition.getIgnoredChars())
                    .directives(definition.getDirectives())
                    .additionalData(definition.getAdditionalData())
                    .comments(definition.getComments())
                    .build();
        }
        return CustomizeField.newBuilder(name).build();
    }

    /**
     * 创建枚举自定义字段
     * @param name {@link String}, 字段名称
     * @param enumType {@link GraphQLEnumType}, 枚举类型
     * @return {@link CustomizeField}
     */
    protected CustomizeField createCustomizeField(String name, GraphQLEnumType enumType){
        EnumTypeDefinition definition = enumType.getDefinition();
        if (definition != null) {
            return CustomizeField.newBuilder(name)
                    .ignoredChars(definition.getIgnoredChars())
                    .directives(definition.getDirectives())
                    .additionalData(definition.getAdditionalData())
                    .comments(definition.getComments())
                    .build();
        }
        return CustomizeField.newBuilder(name).build();
    }

    /**
     * 创建Object类型的自定义字段
     * @param name {@link String}, 字段名称
     * @param objectType {@link GraphQLObjectType}, GraphQL 对象类型
     * @param factory {@link MergedFieldFactory}, 组合字段工厂
     * @return {@link CustomizeField}
     */
    protected CustomizeField createCustomizeField(String name, GraphQLObjectType objectType, MergedFieldFactory<?> factory){
        Map<String, CustomizeField> subFieldMap = new LinkedHashMap<>();
        this.fullEnumAndScalarField(objectType, subFieldMap);

        ObjectTypeDefinition definition = objectType.getDefinition();
        return CustomizeField.newBuilder(name)
                .selectionSet(SelectionSet.newSelectionSet(subFieldMap.values()).build())
                .ignoredChars(definition.getIgnoredChars())
                .directives(definition.getDirectives())
                .additionalData(definition.getAdditionalData())
                .comments(definition.getComments())
                .factory(factory)
                .build();
    }

    /**
     * 创建List类型的自定义字段
     * @param name {@link String}, 名称
     * @param listType {@link GraphQLList}, GraphQL List类型
     * @param factory {@link MergedFieldFactory}, 组合字段工厂
     * @return {@link CustomizeField}
     */
    protected CustomizeField createCustomizeField(String name, GraphQLList listType, MergedFieldFactory<?> factory){
        GraphQLType type = listType.getWrappedType();
        if (type instanceof GraphQLNonNull){
            type = ((GraphQLNonNull)type).getWrappedType();
        }

        if (type instanceof GraphQLScalarType){
            return CustomizeField.newBuilder(name).build();
        }

        if (type instanceof GraphQLEnumType){
            return CustomizeField.newBuilder(name).build();
        }

        if (type instanceof GraphQLObjectType){
            return this.createCustomizeField(name, (GraphQLObjectType)type, factory);
        }

        return null;
    }

}
